#pragma once 

#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <queue>
#include <unistd.h>

struct ThreadData{
    pthread_t _tid;
    std::string _threadname;
};

template<class T>
class ThreadPool{
    static const int defaultnum = 5;

public:
    // 申请锁封装
    void Lock(){
        pthread_mutex_lock(&_mutex);
    }

    // 释放锁封装
    void UnLock(){
        pthread_mutex_unlock(&_mutex);
    }

    // 唤醒封装
    void WakeUp(){
        pthread_cond_signal(&_cond);
    }

    // 条件等待封装
    void ThreadSleep(){
        pthread_cond_wait(&_cond, &_mutex);
    }

    // 判断任务队列是否为空封装
    bool IsTaskEmpty(){
        return _tasks.empty();
    }

    std::string GetThreadName(pthread_t tid){
        for(auto td : _threads){
            if(td._tid == tid){
                return td._threadname;
            }
        }
        return "NONE";
    }

public:
    // 线程的执行函数在类内要设置成静态成员函数
    static void *HandlerTask(void *args){
        // 错误写法：线程的执行函数设置成静态成员函数后，就不能再直接访问类内的成员变量和成员函数
        // while(true){
        //     Lock();
        //     while(_task.empty()){
        //         ThreadSleep();
        //     }
        //     T t = _task.front();
        //     _task.pop();
        //     UnLock();
        //     t.run(); // 每个任务对象在类内有自己的执行方法，所以不用在临界区执行
        // }

        // 正确写法：
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->GetThreadName(pthread_self());

        while(true){
            tp->Lock();
            // 循环判断条件，防止伪唤醒
            while(tp->IsTaskEmpty()){
                tp->ThreadSleep();
            }
            T t = tp->Pop();
            tp->UnLock();
            t.run();
            std::cout << name << " run, result: " << t.GetResult() << std::endl;
        }
    }

    void start(){
        int size = _threads.size();
        for (int i = 0; i < size; i++){
            _threads[i]._threadname = "thread-" + std::to_string(i + 1);
            //pthread_create(&(_threads[i].tid), nullptr, HandlerTask, nullptr);
            // 为了方便线程执行函数直接访问成员变量和成员函数,这里将this指针作为参数传过去
            pthread_create(&(_threads[i]._tid), nullptr, HandlerTask, this);
        }
    }

    // 存放任务到任务队列中
    void Push(const T&t){
        Lock();
        _tasks.push(t);
        WakeUp();
        UnLock();
    }

    // 从任务队列中获取任务
    T Pop(){
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    // 懒汉方式单例模式
    // 静态成员函数只能访问静态成员变量 静态成员变量只有一份，在多线程情况下，就变成了共享资源
    // 需要加上互斥机制进行保护
    static ThreadPool<T>* GetInstance(){
        // 因为多线程只会在初次创建单例时才会竞争_tp指针 如果直接在判断外面加锁
        // 会导致创建之后每次使用单例时都要申请锁释放锁 所以采用双层判断空指针 降低锁冲突的概率 提高性能
        if (_tp == nullptr){
            pthread_mutex_lock(&_s_mutex);
            if (_tp == nullptr){
                _tp = new ThreadPool<T>();
            }
            pthread_mutex_unlock(&_s_mutex);
        }

        return _tp;
    }

private:
    ThreadPool(int num = defaultnum)
    :_threads(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    // 禁用拷贝构造函数和赋值运算符
    // 禁止通过拷贝构造函数创建新的线程池对象
    ThreadPool(const ThreadPool<T> &) = delete;
    // 禁止通过赋值运算符将一个线程池对象那个赋值给另一个
    const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;

private:
    std::vector<ThreadData> _threads;  // 线程数组
    std::queue<T> _tasks;              // 任务队列
 
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *_tp;         // 静态成员变量
    static pthread_mutex_t _s_mutex;   
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;    // 静态成员变量只能在类外初始化

template <class T>
pthread_mutex_t ThreadPool<T>::_s_mutex = PTHREAD_MUTEX_INITIALIZER;